package cn.edu.jxust.core.infrastructure.mqtt.handler;

import cn.edu.jxust.common.domain.dto.AutomaticData;
import cn.edu.jxust.core.infrastructure.OneNetConst;
import cn.edu.jxust.common.domain.dto.RegDeviceVo;
import cn.edu.jxust.common.response.Result;
import cn.edu.jxust.common.response.enums.ResultStatus;
import cn.edu.jxust.core.domain.repository.AirService;
import cn.edu.jxust.core.domain.repository.DeviceService;
import cn.edu.jxust.core.infrastructure.util.AqiUtils;
import cn.edu.jxust.core.infrastructure.util.Const;
import cn.edu.jxust.core.infrastructure.util.LocationUtils;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * @author QiuKangming
 * @version 1.0
 * @description 数据解析实现类
 * @since 2020/07/14 09:29
 */

@Slf4j
@Component("integrationHeaderChannelRegistry")
@SuppressWarnings("unused")
public class MessageArrivedHandler implements MessageHandler {

    @Resource
    private AirService airService;
    @Resource
    private DeviceService deviceService;

    /**
     * 信息处理方法
     * @param msgId   message id
     * @param msgBody message body
     */
    @Override
    public void handle(Long msgId, String msgBody) {

        AutomaticData data = parseData(msgBody);
        if(null != data){
//            log.info("数据解析成功: {}", data);
            switch (data.getHead()){
                case OneNetConst.AU:
                    //如果上传的是经纬度信息
                    if(OneNetConst.LngAndLat.equals(data.getDataType())){
                        //经度 纬度
                        String[] lnglatStr = data.getValue().split("#");
                        double[] temp = computeLngAndLat(lnglatStr);
                        double[] lnglat = LocationUtils.mars2Baidu(temp[0], temp[1]);//0 经度, 1 纬度
                        log.info("更新设备经纬度:[IMEI = " + data.getIMEI() + ",经度: " + lnglat[0]+", 纬度: " + lnglat[1] + "]");
                        deviceService.updateLngLatByIMEI(data.getIMEI(), lnglat);
                    }else{
                        checkData(data);//检查数据并且转换浓度单位
//                        log.info("气体信息存入数据库");
                        Result result = airService.saveByAutoData(data);//保存数据库
                    }
                    break;
                case OneNetConst.AS:
                    log.info("设备发起注册请求,设备IMEI和经纬度:{}{}",data.getIMEI(),data.getValue());
                    //先将信息保存至数据库
                    String[] lnglatStr = data.getValue().split("#");
                    double[] temp = computeLngAndLat(lnglatStr);
                    double[] lnglat = LocationUtils.mars2Baidu(temp[0], temp[1]);//0 经度, 1 纬度
                    Result result = deviceService.saveRegDevice(
                            RegDeviceVo.builder()
                                    .IMEI(data.getIMEI())
                                    .longitude(lnglat[0])
                                    .latitude(lnglat[1])
                                    .regDateTime(LocalDateTime.now())
                                    .build()
                    );
                    if(result.getCode().equals(ResultStatus.SUCCESS.getCode())){
                        log.info("注册信息保存成功, 下发命令到前端...[未完成]");
                        // TODO: 发送命令到前端未完成
                    }
                    break;
            }
        }

    }

    /**
     * 将数据解析成 Automatic 实体
     * @param content 数据字符串
     * @return AutomaticData
     */
    private AutomaticData parseData(String content) {

        JSONObject object = JSONUtil.parseObj(content);
        String body = object.getStr("body");
        String[] arr = body.trim().split(",");
//        log.info("OneNet 接收到数据: {}", body);
        if (arr.length == Const.DATA_LENGTH) {
            String head = arr[0];               //数据头
            String productType = arr[1];        //产品种类
            String IMEI = arr[2];               //NB设备唯一标识
            String dataType = arr[3];           //数据种类(气体名称和经纬度)
            String value = arr[4];              //具体数据值
            return AutomaticData.builder()
                    .head(head)
                    .productType(productType)
                    .IMEI(IMEI)
                    .dataType(dataType)
                    .value(value)
                    .build();
        } else {
            log.warn("获取OneNet数据长度与数据定义格式长度不符");
            return null;
        }

    }

    /**
     * 检查数据, 检查当前气体的浓度与预警值作比较, 超出的数据设置标志位, 有问题的数据暂时先放入redis
     * 然后在达到一定数量的时候推送前端
     * @param data AutomaticData
     */
    private void checkData(AutomaticData data) {
        setAirConcentration(data);
        //这里获取的data说明传送过程中是没问题的, 这里需要检查的是是否有超值的气体浓度信息, 设置 isException 字段
        //如果存在预警信息, 应该将有超值的数据放在 redis 中, 等前端接收通知并且返回的时候就从redis中删除该数据

        //超级管理员: 所有区域报警
        //区级管理员: 自己所在区域报警
        //企业用户: 自己所在企业报警

        // TODO: 数据浓度检测, 报警未完成

        //按照标准检测
        boolean flg = AqiUtils.checkAirConcentration(data.getDataType(), Double.parseDouble(data.getValue()),AqiUtils.HAQI);

        data.setIsException(flg ? 1 : 0);
    }

    /**
     * 设置气体得浓度信息, 设置为系统标准计算的单位: 标准除了 CO 是mg/m3之外,其余的都是 ug/m3
     * @param data 自动数据类型
     */
    private void setAirConcentration(AutomaticData data){

        if(data.getDataType().equals(OneNetConst.PM25) || data.getDataType().equals(OneNetConst.PM10)){
            //PM2.5和PM10无需转换单位
            return;
        }
        double concentration = 0.000000;
        switch (data.getDataType()){
            case OneNetConst.O3:
                concentration = (Double.parseDouble(data.getValue()) * (48 / 22.4)) * 10;
                break;
            case OneNetConst.NO2:
                concentration = (Double.parseDouble(data.getValue()) * (46 / 22.4)) * 10;
                break;
            case OneNetConst.SO2:
                concentration = (Double.parseDouble(data.getValue()) * (64 / 22.4)) * 10;
                break;
            case OneNetConst.CO:
                concentration = (Double.parseDouble(data.getValue()) * (28 / 22.4)) / 10;
                break;
        }
        data.setValue(String.valueOf(concentration));

    }

    /**
     * 转换经纬度
     * @param lnglat 经纬度字符串数组
     * @return double[]
     */
    private double[] computeLngAndLat(String[] lnglat){

        return new double[] {
                Double.parseDouble(lnglat[0].substring(0, 3)) + Double.parseDouble(lnglat[0].substring(3)) / 60,
                Double.parseDouble(lnglat[1].substring(0, 2)) + Double.parseDouble(lnglat[1].substring(2)) / 60
        };

    }

}
